package streaming;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import java.util.Vector;

public class TopologyGenerator {

	/**
	 * Creates a mesh in the Fireflies style - using multiple rings to define
	 * set of neighbors
	 * 
	 * @param N Number of hosts to be created
	 * @param k Numer of rings
	 */
	public static void firefliesTopology(int N, int k) {

		Overlay overlay = Overlay.getInstance();
		
		ArrayList nodesList = new ArrayList(N);

		Host source = new SourceHost();
		overlay.addHost(source);

		Host host;
		for (int i = 1; i < N; i++) {
			host = new Host(i);
			overlay.addHost(host);
			nodesList.add(host);
		}

		// Create k rings - simulate fireflies structure
		ArrayList ring[] = new ArrayList[k];
		Host n1, n2;
		Random rnd = new Random();
		for (int j = 0; j < k; j++) {
			ring[j] = new ArrayList(nodesList);
			
			Collections.shuffle(ring[j], rnd);
			
			// Creates an auxiliar variable to hold the ring distribution. This is done
			// to set the overlay rings controller
			Vector<Integer> ringIds = new Vector<Integer>();
			
			for (int i = 0; i < N - 1; i++) {
				// Get Host id and sets the ringIds
				ringIds.add(i, ((Host)ring[j].get(i)).getId());
				
				n1 = (Host) ring[j].get(i);
				n2 = (Host) ring[j].get((i + 1) % (N - 1));
				n1.addNeighbor(n2.id);
				n2.addNeighbor(n1.id);
				
				n1.addNeighbor(j, n2.id);
				n2.addNeighbor(j, n1.id);
				
			}
			
			overlay.getRings().add(ringIds);
		}

		// Add source's neighbors - unidirectional link
		Host neighbor;
		while (source.getNumberNeighbors() < StreamingConfig.SEEDNEIGHBORS) {
			neighbor = (Host) nodesList.get(rnd.nextInt(N - 1));
			source.addNeighbor(neighbor.id);
			neighbor.addNeighbor(0);
		}
	}

	/**
	 * 
	 * @param N Number of hosts
	 * @param k Number of rings
	 * @param attackType Type of attack to be simulated
	 * @param ratioAttackers Ratio of attackers host
	 */
	public static void firefliesByzTopology(int N, int k, int attackType,
			double ratioAttackers) {

		Overlay overlay = Overlay.getInstance();

		ArrayList nodesList = new ArrayList(N);
		int kMax = 2 * k;

		Host source = new SourceHost();
		overlay.addHost(source);

		int byzCount = (int) Math.round(ratioAttackers * N);
		overlay.countNonByz = N - byzCount;

		Host host;

		// Creates 'correct' hosts. Starts in 1 because 0 is the source host
		for (int i = 1; i < (N - byzCount); i++) {
			host = new Host(i);
			overlay.addHost(host);
			nodesList.add(host);
		}
		
		System.out.println("Peers que omitem: ");
		// Creates attacking hosts
		for (int i = (N - byzCount); i < N; i++) {
			switch (attackType) {
			case 1:
				host = new Attacker1(i);
				break;
			case 2:
				host = new Attacker2(i);
				break;
			case 3:
				host = new Attacker3(i);
				System.out.print(i + ", ");
				break;
			case 4:
				host = new Attacker4(i);
				break;
			case 5:
				host = new Attacker5_Polluter(i);
				break;
			default:
				host = new Host(i);
				break;
			}
			overlay.addHost(host);
			nodesList.add(host);
		}
		System.out.println("");
		
		// Create k rings - simulate fireflies structure
		overlay.ring = new ArrayList[kMax];
		ArrayList ring[] = new ArrayList[k];
		Host n1, n2;
		Random rnd = new Random();
		for (int j = 0; j < k; j++) {
			ring[j] = new ArrayList(nodesList);
			Collections.shuffle(ring[j], rnd);
			
			// Creates an auxiliar variable to hold the ring distribution. This is done
			// to set the overlay rings controller
			Vector<Integer> ringIds = new Vector<Integer>();
			
			for (int i = 0; i < N - 1; i++) {
				// Get Host id and sets the ringIds
				ringIds.add(i, ((Host)ring[j].get(i)).getId());
				
				n1 = (Host) ring[j].get(i);
				n2 = (Host) ring[j].get((i + 1) % (N - 1));
				
				/* Cannot have more than two neighbors by ring */
				if ((n1.neighborQueue.size() >= 2 * k)
						&& (n2.neighborQueue.size() >= 2 * k)) {
					continue;
				}
				if (((n1 instanceof Attacker4) && (n1.neighborQueue.size() >= 1))
						|| ((n2 instanceof Attacker4) && (n2.neighborQueue
								.size() >= 1))) {
					continue;
				}
				n1.addNeighbor(n2.id);
				n2.addNeighbor(n1.id);
				
				n1.addNeighbor(j, n2.id);
				n2.addNeighbor(j, n1.id);
			}
			overlay.getRings().add(ringIds);
		}
		
		// Add source's neighbors - unidirectional link
		Host neighbor;
		while (source.getNumberNeighbors() < StreamingConfig.SEEDNEIGHBORS) {
			neighbor = (Host) nodesList.get(rnd.nextInt(N - byzCount - 1));
			source.addNeighbor(neighbor.id);
			neighbor.addNeighbor(0);
//			overlay.sourceNeighbors.add(neighbor);
		}
	}
}